#include <stdio.h>
/*
I do know i use "inline double" and "const double" too mutch, 
you see, i dont know version of cuda compiller so i want to be shure
i don't det performance issues with this. I'll get it somwhere else -)

if you use gcc, please enable something like -O to pocess inline derectives.
*/


double calcVal(double, double);     	//main calculation rutine
double calcValVerb(double, double); 	//same, with debug printfs

void VerifyControlFunctions(void);  	//service porcedure - checks if raiting functions are good.


int main(void){
    
    double a0 = 0.493155671292032;  	//basic value
    double a1 = 1.001536221854130;  	//basic value
    
    double aG = 1.00153632728887753700;	//tunned value of a1 with 100m iterations up
	   

    double step = 0.000000000000001;	//increment step;

    int i;
    double bestA1 = 0;
    double bestScore = 1.7976931348623158e+308;	//MaxDouble
    double currentScore = 0;

/*
    	//region of testing tuned value in given data set
    printf("reference values \n"); 
    calcValVerb(a0,a1);
    printf("\n optimazed values \n");
    calcValVerb(a0,aG);
*/

   
   
   		//comparation loop;
    for (i = 0; i < 100000000; i++){
	currentScore = calcVal(a0, a1);
	if (currentScore < bestScore) {bestScore = currentScore; bestA1 = a1;}
	a1 = a1 + step;
    }
    printf("Best score = %0.20f, with value a1 of %0.20f \n", bestScore, bestA1);
    	
        //VerifyControlFunctions();
    
    return 0;
}


inline double myAbs(const double Value) { return ((Value * Value) - Value);}
//inline double myAbs(const double Value) { if (Value > 0.0) return Value; else return (0 - Value);}
inline double checkMNKCreterium(const double Yt, const double Yut) { return ((Yt - Yut)*(Yt - Yut));/* no external functions */}
inline double checkModSumm(const double Yt, const double Yut) {return myAbs(Yt - Yut); /* no ext funct */}
inline double checkRel(const double Yt, const double Yut) {return (checkMNKCreterium(Yt,Yut) / Yt);}

void VerifyControlFunctions() {
double Verify[6] = {	2.0, 5.0, 
			2.0, 2.0,
			5.0, 2.0};

	int i; /* it's C'89 */
	for (i = 0; i < 6; i = i+2) {
	printf("%d \n",i);
	printf("%0.3f and %0.3f is %0.3f - MNK\n", Verify[i], Verify[i+1], checkMNKCreterium(Verify[i], Verify[i+1])); 
	printf("%0.3f and %0.3f is %0.3f - MOD\n", Verify[i], Verify[i+1], checkModSumm(Verify[i], Verify[i+1])); 
	printf("%0.3f and %0.3f is %0.3f - REL\n", Verify[i], Verify[i+1], checkRel(Verify[i], Verify[i+1]));
	}
}






inline double calcValB(const double real, const double fc, const double a0){return( a0 * real + ((1.0 - a0) * fc ));}

inline double calcCorrA(const double err, const double corr, const double a1){return( a1 * err + (( 1.0 - a1 ) * corr));}

inline double calcCorrB(const double err, const double corr, const double a0){     //prn printf(" (%0.3f * %0.3f) [%0.3f] + ((%0.3f * %0.3f) [%0.3f] = %0.3f \n", a0, err, (a0*err), (1.0 - a0), corr, ((1 - a0) * corr),( a0 * err + (( 1.0 - a0 ) * corr)));

    return( a0 * err + (( 1.0 - a0 ) * corr));

}

inline double calcValA(const double real, const double fc, const double a1) {return( a1 * real + (( 1.0 - a1) * fc));}


inline double calcFc(const double real, const double fc, const double err, const double corr, const double a0, const double a1){
    double valB =  calcValB(real, fc, a0);
    double corrA = calcCorrB(err, corr, a1);

    //prn printf("Forecast Yb = %0.3f Ea = %0.3f \n",valB,corrA);
    return( valB - corrA );
}


inline double calcCorr(const double real, const double fc, const double err, const double corr, const double a0, const double a1){
    double valA =  calcValA(real, fc, a1);
    double corrB = calcCorrB(err, corr, a0);
    //prn printf("Correctn Ya = %0.3f Eb = %0.3f \n",valA,corrB);
    return( valA + corrB);
}


inline double calcErr(const double real, const double fc){ return (real - fc); }


double calcVal(const double a0,const double a1){

    const int arraySize = 28;  //don't realy need this
    const double realValues[28] = {15.10, 13.20, 14.50, 14.30, 
	15.30, 12.00, 12.90, 15.60, 15.20, 15.70, 14.70, 17.20, 
	24.60, 28.00, 24.10, 25.50, 25.30, 30.40, 27.90, 24.10, 
	24.30, 26.20, 23.80, 22.20, 28.50, 29.00, 32.50, 34.10}; // actual values

    double real = 11.90;	//real value 
    double fc = 13.51843721;	//forecast value
    double err = -1.618437211;	//error value
    double corr = 9.111677835;	//correction value

    double fcNew;		//new forecast value
    double errNew;		//new error value
    double corrNew;		//new correction value

    double howGood = 0.0;		//how good coefficents are

    //prn printf("%0.3f  \t %0.3f \t  %0.3f \t  %0.3f \n", real, fc, err, corr);

    int i;
    for(i=0; i < arraySize; i++){

        double realNew = realValues[i];
        fcNew =   calcFc(real, fc, err, corr, a0, a1);
        errNew =  calcErr(realNew, fcNew);
        corrNew = calcCorr(real, fc, err, corr, a0, a1);

        real = realNew;
        fc = fcNew;
        err = errNew;
        corr = corrNew;

	//creterium zone
	//enable one at the time 
	howGood = howGood + checkMNKCreterium(real, fc);
	//howGood = howGood + checkModSumm(real, fc);
	//howGood = howGood + checkRel(real, fc);


	//comm Prints current iteration 
        //prn printf("%0.3f  \t %0.3f \t  %0.3f \t  %0.3f \n\n", realNew, fcNew, errNew, corrNew);

}
    //comm Prints last iteration
    //prn printf("%0.3f  \t %0.3f  \t %0.3f  \t %0.3f  \n", 0.0, fcNew, errNew, corrNew);
    return howGood;
}


double calcValVerb(const double a0,const double a1){

    const int arraySize = 28;  //don't realy need this
    const double realValues[28] = {15.10, 13.20, 14.50, 14.30, 
	15.30, 12.00, 12.90, 15.60, 15.20, 15.70, 14.70, 17.20, 
	24.60, 28.00, 24.10, 25.50, 25.30, 30.40, 27.90, 24.10, 
	24.30, 26.20, 23.80, 22.20, 28.50, 29.00, 32.50, 34.10}; // actual values 



    double real = 11.90;	//real value 
    double fc = 13.51843721;	//forecast value
    double err = -1.618437211;	//error value
    double corr = 9.111677835;	//correction value

    double fcNew;		//new forecast value
    double errNew;		//new error value
    double corrNew;		//new correction value

    double howGood = 0.0;		//how good coefficents are

    printf("%0.3f  \t %0.3f \t  %0.3f \t  %0.3f \n", real, fc, err, corr);

    int i;
    for(i=0; i < arraySize; i++){

        double realNew = realValues[i];
        fcNew =   calcFc(real, fc, err, corr, a0, a1);
        errNew =  calcErr(realNew, fcNew);
        corrNew = calcCorr(real, fc, err, corr, a0, a1);

        real = realNew;
        fc = fcNew;
        err = errNew;
        corr = corrNew;

	//creterium zone
	//enable one at the time 
	//howGood = howGood + checkMNKCreterium(real, fc);
	//howGood = howGood + checkModSumm(real, fc);
	//howGood = howGood + checkRel(real, fc);


	//comm Prints current iteration 
        printf("%0.3f  \t %0.3f \t  %0.3f \t  %0.3f \n\n", realNew, fcNew, errNew, corrNew);

}
    //comm Prints last iteration
    printf("%0.3f  \t %0.3f  \t %0.3f  \t %0.3f  \n", 0.0, fcNew, errNew, corrNew);

    return howGood;
}




